﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Drawing;
using ITAP.ShprossesBuilder.Modelling.Components.Constraints;
using ITAP.ShprossesBuilder.Calculations;
using ITAP.ShprossesBuilder.Modelling.Measurement;
using ITAP.ShprossesBuilder.Editor;
using ITAP.Core.Modelling;
using ITAP.Core.Calculations;

namespace ITAP.ShprossesBuilder.Modelling.Components
{
    public enum HorizontalAlign
    {
        Left,
        Right,
//        Center,
    }

    public enum VerticalAlign
    {
        Top,
        Bottom,
//        Center,
    }

    [Serializable]
    public class ModelCell : IComponent
    {
        private bool uniqueIdsEnabled = true;

        public bool UniqueIdsEnabled
        {
            get { return uniqueIdsEnabled; }
            set { uniqueIdsEnabled = value; }
        }

        private List<int> acceptedUniqueIdsToConnect;

        public List<int> AcceptedUniqueIdsToConnect
        {
            get { return acceptedUniqueIdsToConnect; }
            set { acceptedUniqueIdsToConnect = value; }
        }

        private int uniqueId;

        public int UniqueId
        {
            get { return uniqueId; }
        }

        private bool inaccessibleFromMaterials = false;

        public bool InaccessibleFromMaterials
        {
            get { return false; }
            set { }
        }


        public string Name
        {
            get { return this.model.Name; }
            set { this.model.Name = value; }
        }

   //     private int serialNumber;

        public int SerialNumber
        {
            get { return model.SerialNumber; }
            set { model.SerialNumber = value; }
        }

        [field: NonSerialized]
        //private IDimensionProcessor dimensionProcessor = new DefaultDimensionProcessor(new Type[] { typeof(Shpross), typeof(Spacer), typeof(Model), typeof(SpacerElement) });

        //public IDimensionProcessor DimensionProcessor
        //{
        //    get { return dimensionProcessor; }
        //    set { dimensionProcessor = value; }
        //}

        private bool autoConnectComponentsInTolerance = true;

        public bool AutoConnectComponentsInTolerance
        {
            get { return autoConnectComponentsInTolerance; }
            set { autoConnectComponentsInTolerance = value; }
        }

        private List<IComponent> componentsInTolerance;

        public List<IComponent> ComponentsInTolerance
        {
            get { return componentsInTolerance; }
        }

        private IComponent parent;

        private RectangleF boundingBox;

        private double offsetX = 0;

        public double OffsetX
        {
            get { return offsetX; }
            set { offsetX = value; }
        }
        private double offsetY = 0;

        public double OffsetY
        {
            get { return offsetY; }
            set { offsetY = value; }
        }

        private HorizontalAlign horizontalModelAlign = HorizontalAlign.Left;

        public HorizontalAlign HorizontalModelAlign
        {
            get { return horizontalModelAlign; }
            set 
            { 
                horizontalModelAlign = value;
                MoveModelTo(value);
            }
        }
        private VerticalAlign verticalModelAlign = VerticalAlign.Bottom;

        public VerticalAlign VerticalModelAlign
        {
            get { return verticalModelAlign; }
            set 
            { 
                verticalModelAlign = value;
                MoveModelTo(value);
            }
        }

        private Model model;

        public Model Model
        {
            get { return model; }
            set { model = value; }
        }

        public ModelCell()
        {
            this.componentsInTolerance = new List<IComponent>();
        }

        #region IComponent Members

        public List<ITAP.ShprossesBuilder.Modelling.Components.Constraints.IConstraint> Constraints
        {
            get { return new List<IConstraint>(); }
        }

        public List<IComponent> ConnectedComponents
        {
            get 
            {
                return new List<IComponent>();
            }
        }

        public List<IComponent> InnerComponents
        {
            get 
            {
                List<IComponent> innerComponents = new List<IComponent>();
                innerComponents.Add(this.model);
                return innerComponents;
            }
        }

        public List<IComponent> AllInnerComponents
        {
            get 
            {
                List<IComponent> components = new List<IComponent>();
                components.Add(model);
                components.AddRange(model.AllInnerComponents);
                return components;
            }
        }

        public double Tolerance
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public RectangleF BoundingBox
        {
            get 
            {
                return this.boundingBox;
            }
            set
            {
                this.boundingBox = value;
            }
        }

        //public bool DimensionProcessorEnabled
        //{
        //    get { return dimensionProcessor.Enabled; }
        //    set { dimensionProcessor.Enabled = value; }
        //}

        public bool IsUpdateRequired
        {
            get { return model.IsUpdateRequired; }
            set { }

        }

        public bool IsChangedSinceLastUpdate
        {
            get { throw new NotImplementedException(); }
        }

        public List<ITAP.Core.Modelling.Primitives.IPrimitive> Primitives
        {
            get { throw new NotImplementedException(); }
        }

        public IComponent ConnectTo(IComponent component)
        {
            throw new NotImplementedException();
        }

        public IComponent DisconnectFrom(IComponent component)
        {
            throw new NotImplementedException();
        }

        public bool CanConnect(IComponent component)
        {
            throw new NotImplementedException();
        }

        public void Update()
        {
            //this.Update(GlobalPreferences.Preferences.EditorDimensions);
            this.model.Update();

            //if (dimensionProcessor == null)
            //    dimensionProcessor = new DefaultDimensionProcessor(new Type[] { typeof(Shpross), typeof(Spacer), typeof(Model), typeof(SpacerElement) }); ;

            //((DefaultDimensionProcessor)dimensionProcessor).SpacerElementDimensionType = DimensionType.OuterDimension;

            //dimensionProcessor.Compute(this, 1, DimensionSpreadingType.ByNearestSide, GlobalPreferences.Preferences.EditorDimensions);
        }

        //public void Update(DimensionsPreferences dimensionsPreferences)
        //{
        //    this.model.Update();

        //    if (dimensionProcessor == null)
        //        dimensionProcessor = new DefaultDimensionProcessor(new Type[] { typeof(Shpross), typeof(Spacer), typeof(Model), typeof(SpacerElement) }); ;

        //    ((DefaultDimensionProcessor)dimensionProcessor).SpacerElementDimensionType = DimensionType.OuterDimension;

        //    dimensionProcessor.Compute(this, 1, DimensionSpreadingType.ByNearestSide, dimensionsPreferences);
        //}

        public void AcceptVisitor(IModelVisitor visitor)
        {
            visitor.Visit(this);
        }

        public bool IsInTolerance(ModelPoint point)
        {
            return this.IsPointInComponent(point);
           // throw new NotImplementedException();
        }

        public List<IComponent> GetComponentsByPoint(ModelPoint point, double tolerance)
        {
            return model.GetComponentsByPoint(point, tolerance);
      //      throw new NotImplementedException();
        }

        public void MoveToZeroPoint()
        {
            RectangleF bbox = this.BoundingBox;
            this.MoveWithOffset(new ModelVector(-bbox.X, -bbox.Y));
        }

        public bool MoveTo(ModelPoint destination)
        {
           // model.MoveTo(destination);

            //return true;
            throw new Exception();
        }

        public bool MoveWithOffset(ModelVector offset)
        {
            model.MoveWithOffset(offset);
            ModelPoint corner = new ModelPoint(this.BoundingBox.X, this.BoundingBox.Y);
            corner = ModelCalculations.GetOffsetPoint(corner, offset);
            this.boundingBox = new RectangleF((float)corner.X, (float)corner.Y, this.boundingBox.Width, this.boundingBox.Height);
            return true;
        }

        public bool Rotate(double angleOffset, ModelPoint rotateCenter)
        {
            throw new NotImplementedException();
        }

        public ModelPoint Position
        {
            get { return new ModelPoint(this.boundingBox.X, this.boundingBox.Y); }
        }

        public ITAP.Core.Modelling.Primitives.EndType GetComponentNearestEndType(IComponent component)
        {
            throw new NotImplementedException();
        }

        public List<Type> PreUpdatePriority
        {
            get { throw new NotImplementedException(); }
        }

        public List<Type> PostUpdatePriority
        {
            get { throw new NotImplementedException(); }
        }

        public IComponent Parent
        {
            get
            {
                return parent;
            }
            set
            {
                parent = value;
            }
        }

        //public bool CheckConstraints()
        //{
        //    throw new NotImplementedException();
        //}

        //public bool CheckCriticalConstraints()
        //{
        //    throw new NotImplementedException();
        //}

        //public bool CheckAllConstraints()
        //{
        //    throw new NotImplementedException();
        //}

        //public bool CheckAllCriticalConstraints()
        //{
        //    throw new NotImplementedException();
        //}

        public bool EnableConstraintsFixing
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public bool EnableAllConstraintsFixing
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public List<Type> CriticalConstraintTypes
        {
            get { throw new NotImplementedException(); }
        }

        public ConnectionInfo AcceptedComponentTypesToConnect
        {
            get { throw new NotImplementedException(); }
        }

        public string Article
        {
            get
            {
                throw new NotImplementedException();
            }
            set
            {
                throw new NotImplementedException();
            }
        }

        public IComponent ConnectToEnded(IEndedComponent endedComponent, ITAP.Core.Modelling.Primitives.EndType endToConnect)
        {
            throw new NotImplementedException();
        }

        public event ITAP.ShprossesBuilder.Modelling.Components.Constraints.ComponentConstraintEventDelegate BeforeFixingConstraint;

        public event ITAP.ShprossesBuilder.Modelling.Components.Constraints.ComponentConstraintEventDelegate AfterFixingConstraint;

        
        public event FatalModelErrorDelegate FatalModelError;

        public bool IsConnected(IComponent other)
        {
            throw new NotImplementedException();
        }

        public void AddInnerComponent(IComponent component)
        {
            model = component as Model;            
            model.Parent = this;
            model.BeforeFixingConstraint += new ComponentConstraintEventDelegate(this.BeforeFixingInnerComponentConstraintEvent);
            model.AfterFixingConstraint += new ComponentConstraintEventDelegate(this.AfterFixingInnerComponentConstraintEvent);
            model.FatalModelError += new FatalModelErrorDelegate(this.FatalModelErrorEvent);
            model.MoveToZeroPoint();
            this.boundingBox = model.BoundingBox;
        }

        public bool RemoveInnerComponent(IComponent component)
        {
            model.Parent = null;
            model.BeforeFixingConstraint -= this.BeforeFixingInnerComponentConstraintEvent;
            model.AfterFixingConstraint -= this.AfterFixingInnerComponentConstraintEvent;
            model.FatalModelError -= this.FatalModelErrorEvent;
            model = null;

            return true;
        }

        public void BeforeFixingInnerComponentConstraintEvent(IComponent sender, ITAP.ShprossesBuilder.Modelling.Components.Constraints.ComponentConstraintEventArgs e)
        {
            if (this.BeforeFixingConstraint != null)
            {
                this.BeforeFixingConstraint.Invoke(sender, e);
            }

        }

        public void AfterFixingInnerComponentConstraintEvent(IComponent sender, ITAP.ShprossesBuilder.Modelling.Components.Constraints.ComponentConstraintEventArgs e)
        {
            if (this.AfterFixingConstraint != null)
            {
                this.AfterFixingConstraint.Invoke(sender, e);
            }
        }

        public void FatalModelErrorEvent(IComponent sender, FatalErrorEventArgs e)
        {
            if (this.FatalModelError != null)
                this.FatalModelError.Invoke(sender, e);
        }

        public ModelPoint GetNearestPointOnComponent(ModelPoint point, StickConnectionType stickType, out StickConnectionType sticked, ProjectionType projectionType)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region ICloneable Members

        public object Clone()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public bool Selected
        {
            get
            {
                return model.Selected;
            }
            set
            {
                model.Selected = value;
            }
        }

        #endregion

        #region IComponent Members


        public bool IsPointInComponent(ModelPoint point)
        {
            PointF _point = new PointF((float)point.X, (float)point.Y);
            RectangleF bbox = this.BoundingBox;
            if (bbox.Contains(_point))
                return true;
            else
                return false;
        }

        #endregion

        #region IModelVisitor Members

        public void Visit(Model model)
        {
         //   throw new NotImplementedException();
        }

        public void Visit(Connector connector)
        {
         //   throw new NotImplementedException();
        }

        public void Visit(Shpross shpross)
        {
         //   throw new NotImplementedException();
        }

        public void Visit(Spacer spacer)
        {
         //   throw new NotImplementedException();
        }

        public void Visit(ShprossConnectorStub stub)
        {
         //   throw new NotImplementedException();
        }

        public void Visit(ShprossConnector shprossConnector)
        {
         //   throw new NotImplementedException();
        }

        public void Visit(SpacerElement spacerElement)
        {
         //   throw new NotImplementedException();
        }

        public void Visit(SpacerConnector spacerConnector)
        {
         //   throw new NotImplementedException();
        }

        public void Visit(ModelCell modelCell)
        {
         //   throw new NotImplementedException();
        }

        public void Visit(ModelContainer modelContainer)
        {
         //   throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members

        public void DisconnectComponentsInTolerance()
        {
            throw new NotImplementedException();
        }

        #endregion


        public void MoveModelTo(VerticalAlign align)
        {
            RectangleF modelBBox = model.BoundingBox;            
            ModelVector offsetVector = null;

            RectangleF bbox = this.BoundingBox;
            switch (align)
            {
                //case VerticalAlign.Center:
                //    model.MoveToZeroYPoint();
                //    offsetVector = new ModelVector(0, bbox.Y + (bbox.Height - modelBBox.Height) / 2 - offsetY);
                //    model.MoveWithOffset(offsetVector);

                //    break;
                case VerticalAlign.Top:

                    model.MoveToZeroYPoint();
                    offsetVector = new ModelVector(0, bbox.Y + bbox.Height - modelBBox.Height - offsetY);
                    model.MoveWithOffset(offsetVector);

                    break;
                case VerticalAlign.Bottom:
                    model.MoveToZeroYPoint();
                    offsetVector = new ModelVector(0, bbox.Y + offsetY);
                    model.MoveWithOffset(offsetVector);
                    break;
                default:
                    return;
            }
        }

        public void MoveModelTo(HorizontalAlign align)
        {
            RectangleF modelBBox = model.BoundingBox;
            RectangleF bbox = this.BoundingBox;
            ModelVector offsetVector = null;
            
            switch (align)
            {
                //case HorizontalAlign.Center:
                //    model.MoveToZeroXPoint();
                //    offsetVector = new ModelVector(bbox.X + (bbox.Width - modelBBox.Width) / 2 + offsetX, 0);
                //    model.MoveWithOffset(offsetVector);
                    
                //    break;
                case HorizontalAlign.Left:
                    model.MoveToZeroXPoint();
                    offsetVector = new ModelVector(bbox.X + offsetX, 0);
                    model.MoveWithOffset(offsetVector);
                    break;
                case HorizontalAlign.Right:
                    model.MoveToZeroXPoint();
                    offsetVector = new ModelVector(bbox.X + bbox.Width - modelBBox.Width - offsetX, 0);
                    model.MoveWithOffset(offsetVector);
                    break;
                default:
                    return;
            }
        }

        public void MoveModelTo(ModelPoint destination)
        {
            throw new NotImplementedException();
        }

        public void DisconnectFromAll()
        {
            List<IComponent> componentsToDisconnect = new List<IComponent>();
            foreach (IComponent connectedComponent in this.ConnectedComponents)
            {
                componentsToDisconnect.Add(connectedComponent);
            }
            foreach (IComponent connectedComponent in componentsToDisconnect)
            {
                ConnectionProvider.Disconnect(this, connectedComponent);
            }


        }


        #region IComponent Members


        public ModelPoint GetNearestPointOnComponent(ModelPoint point, ModelPoint measurePoint, StickConnectionType stickType, out StickConnectionType sticked, ProjectionType projectionType)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(StickConnector stickConnector)
        {
            //throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public void MoveToZeroXPoint()
        {
            throw new NotImplementedException();
        }

        public void MoveToZeroYPoint()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(SnapOnCap snapOnCap)
        {
            //throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members

        public bool SelectByPoint(ModelPoint point)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public void Rotate(double angleOffset)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public ModelPoint GetNearestPointOnComponent(ModelPoint point, StickConnectionType stickType, out StickConnectionType sticked, out IComponent innerComponent, ProjectionType projectionType)
        {
            throw new NotImplementedException();
        }

        public ModelPoint GetNearestPointOnComponent(ModelPoint point, ModelPoint measurePoint, StickConnectionType stickType, out StickConnectionType sticked, out IComponent innerComponent, ProjectionType projectionType)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public bool MoveTo(ModelPoint destination, RectangleF boundingRectangle)
        {
            throw new NotImplementedException();
        }

        public bool MoveWithOffset(ModelVector offset, RectangleF boundingRectangle)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public void RemoveAllInnerComponents()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public bool Rotate(double angleOffset, ModelPoint rotateCenter, RectangleF boundingRectangle)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public bool Rotate(double angleOffset, RectangleF boundingRectangle)
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public bool IsInTolerance(ModelPoint point, double tolerance)
        {
            return this.IsInTolerance(point);
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(ShprossEndToEndConnector endToEndConnector)
        {
            //throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(ShprossChain shprossChain)
        {
    //        throw new NotImplementedException();
        }

        #endregion

        #region IComponent Members


        public IComponent Copy()
        {
            throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(SpacerContainer spacerContainer)
        {
        //    throw new NotImplementedException();
        }

        #endregion

        #region IModelVisitor Members


        public void Visit(CrossConnector crossConnector)
        {
     //       throw new NotImplementedException();
        }

        #endregion
    }
}
